BOOT_SEG equ 0x7c0
STACK_SIZE equ 512
SYSTEM_SEG equ 0x1000  ; 0x1000*16+0=0x10000: the location system code is loaded
SYSTEM_LEN equ 16  ; number of sectors the system code consumes
; 16*512=0x2000, so system code will be in [0x10000, 0x12000)
; after move, system code will be in [0, 0x2000)
; current code in [0x7c00, 0x7c00+512sector+512stack=0x8000)

  jmp BOOT_SEG:start  ; physical address = segment address * 16 + offset!!
start:
  mov ax, cs
  mov ds, ax
  mov es, ax
  mov ss, ax
  mov sp, end+STACK_SIZE

load_system:
  mov dx, 0x0000  ; dh: head number; dl: driver number
  mov cx, 0x0002  ; ch: low 8 bits of the 10-bit track number;
                  ; cl: bit 7,6 is the high 2 bits of the track number, bit 5-0
                  ;     is the number of the beginning sector (start from 0)
  mov ax, SYSTEM_SEG
  mov es, ax
  xor bx, bx  ; es:bx is the memory buffer location
  mov ah, 2  ; ah: the function number for reading sectors
  mov al, SYSTEM_LEN  ; al: number of sectors to be read
  int 0x13
  jnc load_ok
die:
  jmp die

load_ok:
  cli  ; turn off interrupt
  mov ax, SYSTEM_SEG  ; ds:si==>es:di
  mov ds, ax
  xor ax, ax
  mov es, ax
  mov cx, 512*SYSTEM_LEN/2  ; 4KB/2B
  sub si, si
  sub di, di
  rep movsw
; since 0x0040 is the start of BIOS data, once we use 'rep movsw' to overwrite
; it, it's not possible (?) to use BIOS's interrupt functions, that's why the
; 'show_data' section does not work, and why we need to load the system into
; another place first instead of loading it directly into 0x0.

  mov ax, BOOT_SEG
  mov ds, ax
  lidt idt_48
  lgdt gdt_48
  mov ax, 0x1  ; bit-0 in cr0 is PE for enabling protected mode
  lmsw ax  ; ax[0..3]->cr0[0..3]
  jmpi 0, 8

gdt:
  dw 0, 0, 0, 0  ; first descriptor is not used

  dw 0x07ff  ; seg len=2047
  dw 0x0000  ; seg base=0
  dw 0x9a00  ; code seg, readable/executable
  dw 0x0000  ; granularity=4KB

  dw 0x07ff  ; seg len=2047
  dw 0x0000  ; seg base=0
  dw 0x9a00  ; data seg, readable/writable
  dw 0x0000  ; granularity=4KB

idt_48:
  dw 0  ; idt table len=0
  dw 0, 0  ; linear address of idt table is also 0
gdt_48:
  dw 0x7ff  ; gdt table len=2KB, =256 descriptors
  dw 0x7c00+gdt, 0  ; linear address of gdt table is 0x7c00+gdt

  times 510 - ($ - $$) db 0
  dw 0xaa55
